/*
 * File:   mcQuaternion.h
 * Author: thales
 *
 * Created on September 30, 2009, 5:33 PM
 */



#include "mcMatrix.h"

using namespace mc;


/**
 * @brief Construtor padrão, constroi uma matriz 2x2 com o valor passado
 *
 * @param Valor a ser setado em todas as posições
 */
template <class T>
matrix2<T>::matrix2( const T &t ) {
    this->_00 = t; this->_01 = t;
    this->_10 = t; this->_11 = t;
}


/**
 * @brief Construtor padrão, constroi uma matriz identidade
 *
 * @param Valor a ser setado em todas as posições
 */
template <class T>
matrix2<T>::matrix2() {
    this->make_identity();
}


/**
 * @brief Construtor explicito, constroi uma matriz 2x2 com os valores passados
 *
 * @param __00 - Posição 00
 * @param __01 - Posição 01
 * @param __11 - Posição 11
 * @param __12 - Posição 12
 */
template <class T>
matrix2<T>::matrix2(
    const T __00, const T __01,
    const T __10, const T __11
)
: _00(__00), _01(__01),
  _10(__10), _11(__11)
{}


/**
 * @brief Construtor por cópia de vetor, constroi uma matriz 2x2 com o vetor passado
 *
 * @param m - Matriz a ser copiada
 */
template <class T>
matrix2<T>::matrix2( const T *m ) {
    this->set_data(m);
}


/**
 * @brief Construtor por cópia de matriz.
 *
 * @param m - Matriz a ser copiada
 */
template <class T>
matrix2<T>::matrix2( const matrix2<T> &m ) {
    this->_00 = m._00; this->_01 = m._01;
    this->_10 = m._10; this->_11 = m._11;
}


/**
 * @brief Construtor por cópia de matriz. Retira uma matriz 2x2 de uma 3x3
 *
 * @param m - Matriz a ser copiada
 */
template <class T>
matrix2<T>::matrix2( const matrix3<T> &m ) {
    this->_00 = m._00; this->_01 = m._01;
    this->_10 = m._10; this->_11 = m._11;
}


/**
 * @brief Construtor por cópia de matriz. Retira uma matriz 2x2 de uma 4x4
 *
 * @param m - Matriz a ser copiada
 */
template <class T>
matrix2<T>::matrix2( const matrix4<T> &m ) {
    this->_00 = m._00; this->_01 = m._01;
    this->_10 = m._10; this->_11 = m._11;
}


/**
 * @brief Acessa um elemento da matriz
 *
 * @param i - Acesso a linha
 * @param j - Acesso a coluna
 */
template <class T>
T matrix2<T>::get_element( int i, int j ) {
    assert( i >= 0 && i < this->get_row_count() && j >= 0 && j < this->get_col_count() );
    return this->_array[this->get_col_count() * j + i];
}

/**
 * @brief Seta um elemento da matriz 2x2
 *
 * @param i - Linha
 * @param j - Coluna
 * @param e - Elemento a ser setado
 */
template <class T>
void matrix2<T>::set_element( int i, int j, T e ) {
    assert( i >= 0 && i < this->get_row_count() && j >= 0 && j < this->get_col_count() );
    this->_array[this->get_col_count() * i + j] = e;
}


/**
 * @brief Cria uma matriz identidade 2x2
 */
template <class T>
void matrix2<T>::make_identity() {
    this->_00 = (T) 1; this->_01 = (T) 0;
    this->_10 = (T) 0; this->_11 = (T) 1;
}

/**
 * @brief Operador de soma de matrizes 2x2
 *
 * @param m1
 * @param m2
 */
template <class T>
matrix2<T>& matrix2<T>::operator + ( const matrix2<T>& m ) {
    matrix2<T> *mr = new matrix2<T>( (T) 0 );
    mr->_00 = this->_00 + mr._00;
    mr->_01 = this->_01 + mr._01;
    mr->_10 = this->_10 + mr._10;
    mr->_11 = this->_11 + mr._11;
    return *mr;
}


/**
 * @brief Operador de subtração de matrizes 2x2
 *
 * @param m1
 * @param m2
 */
template <class T>
matrix2<T>& matrix2<T>::operator - ( const matrix2<T>& m ) {
    matrix2<T> *mr = new matrix2<T>( (T) 0 );
    mr->_00 = this->_00 - m._00;
    mr->_01 = this->_01 - m._01;
    mr->_10 = this->_10 - m._10;
    mr->_11 = this->_11 - m._11;
    return *mr;
}

/**
 * @brief Operador de multiplicação de matrizes 2x2
 *
 * @param m1
 * @param m2
 */
template <class T>
matrix2<T>& matrix2<T>::operator * ( const matrix2<T>& m ) {
    matrix2<T> *mr = new matrix2<T>( (T) 0 );
    mr->_00 = this->_00 * m._00 + this->_01 * m._10;
    mr->_01 = this->_00 * m._01 + this->_01 * m._11;
    mr->_10 = this->_10 * m._00 + this->_11 * m._10;
    mr->_11 = this->_10 * m._01 + this->_11 * m._11;
    return *mr;
}

/**
 * @brief Operador de soma de matrizes 2x2
 *
 * @param m1
 * @param m2
 */
template <class T>
matrix2<T>& matrix2<T>::operator += ( const matrix2<T>& m ) {
    this->_00 += m._00;
    this->_01 += m._01;
    this->_10 += m._10;
    this->_11 += m._11;
    return *this;
}


/**
 * @brief Operador de subtração de matrizes 2x2
 *
 * @param m1
 * @param m2
 */
template <class T>
matrix2<T>& matrix2<T>::operator -= ( const matrix2<T>& m ) {
    this->_00 -= m._00;
    this->_01 -= m._01;
    this->_10 -= m._10;
    this->_01 -= m._01;
    return *this;
}


/**
 * @brief Operador de multiplicação de matrizes 2x2
 *
 * @param m1
 * @param m2
 */
template <class T>
matrix2<T>& matrix2<T>::operator *= ( const matrix2<T>& m ) {
    matrix2<T> *mr = new matrix2<T>( (T) 0 );
    mr->_00 = this->_00 * m._00 + this->_01 * m._10;
    mr->_01 = this->_00 * m._01 + this->_01 * m._11;
    mr->_10 = this->_10 * m._00 + this->_11 * m._10;
    mr->_01 = this->_10 * m._01 + this->_11 * m._11;
    return *mr;
}



/**
 * @brief Transpõe a matriz 2x2
 */
template <class T>
void matrix2<T>::transpose() {
    matrix2<T> m(this->get_data());
    /// Transpoẽ os dois elementos que serão modificados
    this->_01 = m._10;
    this->_10 = m._01;
}


/**
 * @brief Transpõe a matriz 2x2
 */
template <class T>
void matrix2<T>::transpose( matrix2<T> &m ) {
    /// Transpoẽ os dois elementos que serão modificados
    this->_01 = m._10;
    this->_10 = m._01;
}


/**
 * @brief Retorna o determinante da matriz 2x2
 *
 * @return O determinante da matriz
 */
template <class T>
T matrix2<T>::determinant() {
    return (this->_00 * this->_11 - this->_01 * this->_10);
}


/**
 * @brief Retorna o vetor com os dados da matriz 2x2
 *
 * @return Os dados da matriz
 */
template <class T>
T* matrix2<T>::get_data() {
    return this->_array;
}


/**
 * @brief Seta o vetor com os dados da matriz 2x2
 *
 * @return Os dados da matriz
 */
template <class T>
void matrix2<T>::set_data( const T *array ) {
    for( register unsigned int i = 0; i < this->get_size(); i++) this->_array[i] = array[i];
}


/**
 * @brief Calcula a inversa desta matriz
 *
 * @return true se a matriz tiver inversa e falso caso contrário
 */
template <class T>
bool matrix2<T>::inverse() {
    matrix2<T> m(this->get_data());
    /// Inverte a diagonal principal
    this->_00 = -m._11;
    this->_11 = -m._00;

    T det = this->determinant();

    if( det <= (T) 0 ) return false;

    this->_00 /= det;
    this->_01 /= det;
    this->_10 /= det;
    this->_11 /= det;

    return true;
}

/**
 * @brief Calcula a inversa de uma matriz
 *
 * @return true se a matriz tiver inversa e falso caso contra
 */
template <class T>
bool matrix2<T>::inverse( matrix2<T> &m ) {
    /// Inverte a diagonal principal
    this->_00 = -m._11;
    this->_11 = -m._00;

    T det = this->determinant();

    if( det <= (T) 0 ) return false;

    this->_00 /= det;
    this->_01 /= det;
    this->_10 /= det;
    this->_11 /= det;

    return true;
}